/*
 * File:   MAPFile.h
 * Author: jeffrey
 *
 * Created on May 13, 2010, 6:22 PM
 */

#ifndef _MAPFILE_H

#define	_MAPFILE_H
/* UniApo
 * File:   MAPFile.h
 * Author: Jeffrey Geyssens
 *
 * Created on April 26, 2010, 6:09 PM
 * MAP file is created by the user or loaded from file.
 * This object is created and used by the MAPEditor.
 * Manually modifying the physical file using a OS based text editor
 * may provoke errors when reading to the object, you have been warned!
 *
 */

#include <iostream> //for input/output
#include <string> //for string operations
#include <fstream> //for files
#include <vector>
#include "SCBFile.h" //Also loads DRT
#include "DBSFile.h" //load section template
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "DRTSection.h"
#include "RTSection.h"
#include "StorageRuleContainer.h"
#include "UniApoFile.h"

using namespace std;

class MAPFile : public UniApoFile {
public:
    //constructor, destructor and copy constructor
    MAPFile();
    MAPFile(const MAPFile& orig);
    virtual ~MAPFile();

    //MAP a SCB and DBS.
    //Populates real and test db using DBS
    //
    void autogenerate(string SCBpath, string DBSpath);

    //Operations involving DRT
    void addDRT(string DRT);
    void modifyDRT(string DRT, int entryNo); //TODO: modDRT
    void removeDRT(int entryNo); //TODO: rmDRT
    void setDRTId(string DRT, string id); //Huh, perhaps remove
    void setDRTTable(string DRT, string table);
    void setDRTPK(string DRT, string PK);
    void setDRTSymRef(string DRT, string sref);
    void setDRTDefRef(string DRT, string dref);
    void printDRT();
    void printDRT(int entryNp);
    string getDRTid(int entryNo);

    //Copied PRcontainer editing
    void printPRule();
    //Adding elements directly to DRT section
    //Saves results in SCB too
    void addSD(string s, string d, string m, string DRT);
    void modSD(string s, string d, string m, string DRT, int entryNo);
    void delSD(int entryNo);
    ProductionRuleContainer& getPR();

    //Not needed eh?
    void bulkDRT(string path);

    //Operations for RT
    void addRT(string RT);
    void bulkRT(string RT);
    void modifyRT(string RT, int entryNo); //TODO: modRT
    void removeRT(int entryNo); //TODO: modRT
    void setRTId(string RT, string id);
    void setRTTable(string RT, string table);
    void setRTAttrib(string RT, string sref);
    void printRT();
    void printRT(int entryNp);
    string getRTid(int entryNo);

    //Operations regarding sentence structures
    void addStructure(string struc);
    void addPSQL(string struc, string psql);
    void printSRULE();
    void printSRULE(string sentence);
    string getSRULEid(int entryNo);
    //    void processPDS(PDSFile PDS);
    vector<DMLStatement> getPSQL(string struc);


    //Build file from object, store results at given path.
    void buildFile();
    //Print file contents to screen.
    void print();
    //Load file from given path in to this object.
    void loadFile();
    void createDB();

    void name(string n);

    /*
   void setPR(ProductionRuleContainer& pr) {
        PRsec = pr;
    }
     */
    SCBFile& getSCB() {
        return sourceSCB;
    }

    DBSFile& getDBS() {
        return sourceDBS;
    }

    void setSCB(SCBFile& SCB) {
        sourceSCB = SCB;
    }

    void setDBS(DBSFile& DBS) {
        sourceDBS = DBS;
    }
    //For Parser:
    void buildParser(string pname);

private:
    DRTSection DRTsec;
    RTSection RTsec;
    StorageRuleContainer SSsec;

    SCBFile sourceSCB;
    DBSFile sourceDBS;

    void addPRule(string NT, string deriv);
    void addDRT(DataReferenceTerminal& DRT);
    void addRT(ReferenceTerminal& RT);
    void buildAccent(string pname);
    void copyScript(string pname);
    void parserScript(string pname);


};

MAPFile::MAPFile() {

}

MAPFile::MAPFile(const MAPFile& orig) {
    filename = orig.filename;
    extension = orig.extension;
    path = orig.path;
    scriptpath = orig.path;
    DRTsec = orig.DRTsec;
    RTsec = orig.RTsec;
    SSsec = orig.SSsec;
    sourceSCB = orig.sourceSCB;
    sourceDBS = orig.sourceDBS;

}

MAPFile::~MAPFile() {
}

void MAPFile::autogenerate(string SCBname, string DBSname) {

    sourceSCB.name(SCBname);
    sourceDBS.name(DBSname);
    sourceSCB.loadFile();
    sourceDBS.loadFile();

    vector<SymbolDefinition> SDvect;
    //TODO: Write using iterator from section, very ugly now
    SDvect = sourceSCB.getSD().getSectionData();
    vector<SymbolDefinition>::iterator It1;
    It1 = SDvect.begin();
    for (It1 = SDvect.begin(); It1 != SDvect.end(); ++It1) {
        SymbolDefinition& SD = (*It1);
        DRTsec.addSymDef(SD);
    }
    /*
    cout << "UniApo will now create local test DB." << endl;
    cout << "Warning: This will effectively DROP all tables currently residing";
    cout << "in the test DB specified in the DB configuration file." << endl;
    cout << "You can specify which test database to use in the DBconfig file.";
    cout << endl;

    char cont = 'A';
    while ((cont != 'y') && (cont != 'n')) {
        cout << "Do you wish to continue?(y/n): ";
        if (cont == 'y') sourceDBS.createtestDB();
    }
     */

    //sourceDBS.createtestDB();
}

void MAPFile::addDRT(string DRT) {
    DRTsec.newDRT(DRT);
    DRTsec.printDRTlist();
}

void MAPFile::modifyDRT(string DRT, int entryNo) {

}

string MAPFile::getDRTid(int entryNo) {
    return DRTsec.getDRTid(entryNo);

}

void MAPFile::setDRTTable(string DRT, string table) {
    DRTsec.setTable(DRT, table);
}

void MAPFile::setDRTPK(string DRT, string PK) {
    DRTsec.setPK(DRT, PK);
}

void MAPFile::setDRTSymRef(string DRT, string s) {
    DRTsec.setSymAttrib(DRT, s);
}

void MAPFile::setDRTDefRef(string DRT, string d) {
    DRTsec.setDefAttrib(DRT, d);
}

void MAPFile::printDRT() {
    this->DRTsec.printDRTlist();
}

void MAPFile::printDRT(int entryNo) {
    this->DRTsec.printDRTlist(entryNo);
}

void MAPFile::addSD(string s, string d, string m, string DRT) {
    sourceSCB.addSymDef(s, d, m, DRT);
    SymbolDefinition x;
    x.setSymbol(d);
    x.setDefinition(d);
    x.setMask(m);
    x.setDRT(DRT);
    DRTsec.addSymDef(x);
}

void MAPFile::modSD(string s, string d, string m, string DRT, int entryNo) {

}

void MAPFile::delSD(int entryNo) {

}

void MAPFile::addRT(string RT) {
    RTsec.newRT(RT);
}

string MAPFile::getRTid(int entryNo) {

    return RTsec.getRTid(entryNo);

}

void MAPFile::setRTTable(string RT, string table) {
    RTsec.setTable(RT, table);
}

void MAPFile::setRTAttrib(string RT, string s) {
    RTsec.setAttrib(RT, s);
}

void MAPFile::printRT() {
    this->RTsec.printRTlist();
}

void MAPFile::printRT(int entryNo) {
    this->RTsec.printRTlist(entryNo);
}
//TO REMOVE perhaps or at least expand using friendship

void MAPFile::addPRule(string NT, string deriv) {
    sourceSCB.addPRule(NT, deriv);
    //PRsec.addProduction(NT, deriv);
}

void MAPFile::buildFile() {
    ofstream myfile;
    myfile.open(path.c_str());
    myfile << "filename: " << filename.c_str() << ".";
    myfile << extension.c_str() << "\n";
    myfile << "SCB: " << sourceSCB.getFilename().c_str() << ".";
    myfile << sourceSCB.getFileType().c_str() << "\n";
    myfile << "DBS: " << sourceDBS.getFilename().c_str() << ".";
    myfile << sourceDBS.getFileType().c_str() << "\n\n";
    myfile.close();


    DRTsec.buildFile(path);
    RTsec.buildFile(path);
    SSsec.buildFile(path);

    //TO-DO: save in SCB when editing DRT
    sourceSCB.buildFile();
}

void MAPFile::loadFile() {

    ifstream MAPFile(path.c_str());
    string line;
    int i;



    //first process header
    getline(MAPFile, line); //first line is mapname
    line = line.substr(10, line.size()); //ommit "filename:"
    filename = line.substr(0, line.size() - 4);
    extension = "MAP";
    getline(MAPFile, line); //then the SCBpath (remove some day)

    line = line.substr(4, line.size()); //ommit "SCB:"
    while (line[0] == ' ')
        line.erase(0, 1);

    //ommit extension
    i = line.find(".SCB");
    line = line.substr(0, i);
    sourceSCB.name(line);
    getline(MAPFile, line); //then the extension (remove some day)


    line = line.substr(4, line.size()); //ommit "SCB:"

    while (line[0] == ' ')
        line.erase(0, 1);

    //ommit extension
    i = line.find(".DBS");
    line = line.substr(0, i);

    sourceDBS.name(line);

    //Load SCB and DBS contents in container
    sourceSCB.loadFile();
    sourceDBS.loadFile();


    cout << "Reading DRT section" << endl;


    while (line != ".DRTMATCH") {
        getline(MAPFile, line);

    }
    while (line != ".END_DRTMATCH") {
        getline(MAPFile, line);

        if (line == ".DRT") {
            getline(MAPFile, line); //id
            string drt, table, pk, sa, da;
            drt = line.substr(4, line.size());
            getline(MAPFile, line); //table
            table = line.substr(4, line.size());
            getline(MAPFile, line); //PK
            pk = line.substr(4, line.size());
            getline(MAPFile, line); //symbol attribute
            sa = line.substr(4, line.size());
            getline(MAPFile, line); //definition attribute
            da = line.substr(4, line.size());

            while (line != ".END_DRT") {
                SymbolDefinition SD;
                string s;
                string d;
                string m;
                getline(MAPFile, line);
                int i;
                int j;
                j = line.find("\t");
                s = line.substr(0, j);
                SD.setSymbol(s);
                for (i = 0; i <= j; i++)
                    line.erase(line.begin());

                j = line.find("\t");
                d = line.substr(0, j);
                SD.setDefinition(d);
                for (i = 0; i <= j; i++)
                    line.erase(line.begin());
                j = line.find("\n");
                m = line.substr(0, j);
                SD.setMask(m);
                SD.setDRT(drt);
                for (i = 0; i <= j; i++)
                    line.erase(line.begin());
                if (SD.getSymbol() != ".END_DRT") {
                    DRTsec.addSymDef(SD);
                }
                DRTsec.setTable(drt, table);
                DRTsec.setPK(drt, pk);
                DRTsec.setSymAttrib(drt, sa);
                DRTsec.setDefAttrib(drt, da);
            }
        }
    }

    cout << "Reading RT section" << endl;

    while (line != ".RTMATCH") {
        getline(MAPFile, line);
    }

    while (line != ".END_RTMATCH") {
        getline(MAPFile, line);

        if (line == ".RT") {

            ReferenceTerminal d; //process rt header

            getline(MAPFile, line); //id
            string rt;
            rt = line.substr(4, line.size());
            RTsec.newRT(rt);
            getline(MAPFile, line); //table
            RTsec.setTable(rt, line.substr(4, line.size()));
            getline(MAPFile, line); //attribute
            RTsec.setAttrib(rt, line.substr(4, line.size()));

        }
    }
    cout << "Testing Storage Rule Container integrity" << endl;

    while (line != ".SRULE") {
        getline(MAPFile, line);

    }
    while (line != ".END_SRULE") {
        getline(MAPFile, line);
        if (line == ".STRUCT") {


            //DataReferenceTerminal d; //process DRT header
            getline(MAPFile, line); //reference
            line.erase(0, 4);
            string struc = line;

            SSsec.loadStructure(struc);
            while (line != ".END_STRUCT") {
                getline(MAPFile, line);
                int test;

                test = line.find("INSERT INTO");
                if (test != line.npos) {
                    SSsec.addPStatement(struc, line);
                }

            }
        }
    }

    MAPFile.close();

}

void MAPFile::name(string n) {
    this->filename = n;
    this->extension = "MAP";
    this->setPath();
}

void MAPFile::print() {
    this->DRTsec.print();
    this->RTsec.print();
    this->SSsec.print();
}

void MAPFile::addStructure(string struc) {
    //TO-DO: check STRUCTURE for errors from MAP.addStructure
    SSsec.addStructure(struc);
}

void MAPFile::addPSQL(string struc, string psql) {
    SSsec.addPStatement(struc, psql);
}

void MAPFile::printSRULE() {
    SSsec.printSRULElist();
}

void MAPFile::printSRULE(string s) {
    SSsec.printSRULElist(s);
}

string MAPFile::getSRULEid(int entryNo) {
    return SSsec.getSRULEid(entryNo);
}

vector<DMLStatement> MAPFile::getPSQL(string struc) {
    return SSsec.getSQL(struc);
}

//This procedure builds the parser using a MAP file

void MAPFile::buildParser(string pname) {
    //pname is the given name of the parser
    //we store everything in /UniApo/accent/<pname>
    //Create the path
    char *p = NULL;
    p = getcwd(NULL, 0);
    string p2 = p;
    string::size_type loc3 = p2.find("UniApo", 0);
    string fullpath = p2.substr(0, loc3 + 6);
    fullpath.append("/Parser/");
    //Load the shell script for copying source files and building parser
    fullpath.append(pname);
    string ppath;
    ppath = fullpath;
    fullpath.append("/spec.acc");

    cout << fullpath << endl;

    this->copyScript(pname);
    this->buildAccent(fullpath);
    this->parserScript(pname);


}
//Build Accent specification file

void MAPFile::buildAccent(string spec) {
    //Create parser folder in /accent/pname
    cout << spec << endl;
    ofstream myfile;
    myfile.open(spec.c_str());

    //First comes the prelude
    myfile << "%prelude {\n";
    myfile << "#include <stdio.h> \n#include <stdlib.h>\n#include \"extra.h\"";
    myfile << "\n}\n";
    myfile << "%token COMMENT, STRMOD;\n";
    myfile.close();

    //Combine ProductionRules and DRTSection to construct majority of the
    //spec file.
    sourceSCB.buildSpec(spec);
    DRTsec.buildSpec(spec);
    RTsec.buildSpec(spec);

    //Filestream for extra productionrules
    ofstream xfile(spec.c_str(), ios::app);

    //Clear Stored Terminals Container
    xfile << "CLEAR:\n";
    xfile << "{clearDRT();}\n";
    xfile << ";\n";
    //Print Stored Terminal Container
    xfile << "PRINT:\n";
    xfile << "{copyContainer();}\n";
    xfile << ";\n";
    //Production Rules for numbers
    xfile << "NUM:\n";
    xfile << "'0' NUM\n";
    xfile << "| '1' NUM\n";
    xfile << "| '2' NUM\n";
    xfile << "| '3' NUM\n";
    xfile << "| '4' NUM\n";
    xfile << "| '5' NUM\n";
    xfile << "| '6' NUM\n";
    xfile << "| '7' NUM\n";
    xfile << "| '8' NUM\n";
    xfile << "| '9' NUM\n";
    xfile << "| '0' \n";
    xfile << "| '1' \n";
    xfile << "| '2' \n";
    xfile << "| '3' \n";
    xfile << "| '4' \n";
    xfile << "| '5' \n";
    xfile << "| '6' \n";
    xfile << "| '7' \n";
    xfile << "| '8' \n";
    xfile << "| '9' \n";
    xfile << ";\n";
    xfile.close();
}

void MAPFile::copyScript(string pname) {
    system("chmod +x buildparser");
    system("chmod 777 buildparser");
    stringstream buildparser;
    buildparser << "./buildparser " << pname;
    system(buildparser.str().c_str());
}

void MAPFile::parserScript(string pname) {
    system("chmod +x buildp");
    system("chmod 777 buildp");
    stringstream buildp;
    buildp << "./buildp " << pname;
    system(buildp.str().c_str());
}

void MAPFile::bulkRT(string path) {
    //TO: Use substring instead of erase for bulk files
    string line;
    string RT;
    string table;
    string attrib;

    int i;
    int j;
    ifstream csvbulk(path.c_str());

    if (csvbulk.is_open()) {
        cout << "Processing File..." << endl;
    } else {
        cout << "No such file at specified location" << endl;
    }

    while (csvbulk.is_open()) {
        if (!csvbulk.eof()) {
            getline(csvbulk, line);

            j = line.find(";");
            RT = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find(";");
            table = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = table.find("\n");
            attrib = line.substr(0, j);

            if (RT != "") {
                this->addRT(RT);

                if (table != "") {
                    this->setRTTable(RT, table);
                    if (attrib != "") {
                        this->setRTAttrib(RT, attrib);
                    }
                }

            }

        } else {
            csvbulk.close();
        }
    }
}

void MAPFile::bulkDRT(string path) {

    string line;
    string DRT;
    string table;
    string PK;
    string sattrib;
    string dattrib;
    int i;
    int j;

    ifstream csvbulk(path.c_str());

    if (csvbulk.is_open()) {
        cout << "Processing File..." << endl;
    } else {
        cout << "No such file at specified location" << endl;
    }

    while (csvbulk.is_open()) {
        if (!csvbulk.eof()) {
            getline(csvbulk, line);

            j = line.find(";");
            DRT = line.substr(0, j);
            //just a DRT find here

            for (i = 0; i <= j; i++)
                line.erase(line.begin());

            j = line.find(";");
            table = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find(";");
            PK = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find(";");
            sattrib = line.substr(0, j);
            for (i = 0; i <= j; i++)
                line.erase(line.begin());
            j = line.find("\n");
            dattrib = line.substr(0, j);

            if (DRT != "") {
                if ((table != "") && (table != "#")) {
                    this->DRTsec.setTable(DRT, table);
                }
                if ((sattrib != "") && (sattrib != "#")) {
                    this->DRTsec.setSymAttrib(DRT, sattrib);
                }
                if ((dattrib != "") && (dattrib != "#")) {
                    this->DRTsec.setDefAttrib(DRT, dattrib);
                }
                if ((PK != "") && (PK != "#")) {
                    this->DRTsec.setPK(DRT, PK);
                }

            }

        } else {
            csvbulk.close();
        }
    }
}

void MAPFile::createDB() {
    //First display warning, we are about to destroy the old test database



    //First we get all data
    vector<string> DRTDML = this->DRTsec.getDML();
    //Append to DBS file

    //Then Execute from DBS file

}

#endif	/* _MAPFILE_H */
